#include <stdio.h>
//内存函数
//1️⃣memcpy    (memory copy)
//功能：
//是完成内存块拷贝的，不关注内存中存放的数据是什么；
//从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
//如果source和destination有任何的重叠，复制的结果都是未定义的（内存重叠的情况使用memmove）。
void* memcpy (void* destination,const void* source,size_t num ); 
//参数：destination指向目标空间，拷贝的数据存放在这里；source指向源空间，要拷贝的数据从这里来；num要拷贝的数据占据的字节数
//返回值：拷贝完成后，返回目标空间的起始地址
//代码演示：
#include <string.h>
int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10};
    int brr[20]={0};
    memcpy(brr,arr,40);
    int i;
    for(i=0;i<20;i++)
    {
        printf("%d ",brr[i]);//输出:1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 
    }
    printf("\n");
    memcpy(brr+10,arr,16);
    for(i=0;i<20;i++)
    {
        printf("%d ",brr[i]);//输出:1 2 3 4 5 6 7 8 9 10 1 2 3 4 0 0 0 0 0 0
    }
    printf("\n");
    memcpy(arr+2,arr,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",arr[i]);//输出:1 2 1 2 3 4 7 8 9 10
    }
    printf("\n");
    float arr1[]={1.2f,2.3f,4.5f,6.6f,8.8f};
    float brr1[5]={0};
    memcpy(brr1,arr1,20);
    for(i=0;i<5;i++)
    {
        printf("%f ",brr1[i]);//输出:1.200000 2.300000 4.500000 6.600000 8.800000
    }
    return 0;
}
//模拟实现：
void* my_memcpy(void* dest,const void* str,size_t num) //泛型编程
{
    int i;
    for(i=0;i<num;i++)
    {
        *((char*)dest+i)=*((char*)str+i);//一个字节一个字节拷贝，类似vscode 8.3.c 209行 P96~97
    }
    return dest;
}
int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10};
    int brr[20]={0};
    my_memcpy(brr,arr,4);
    int i;
    for(i=0;i<20;i++)
    {
        printf("%d ",brr[i]);//输出:1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    }
    printf("\n");
    my_memcpy(arr+2,arr,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",arr[i]);//输出:1 2 1 2 1 2 7 8 9 10 
    }
    return 0;
}
//从第30行和第66行以及33行和69行可以发现：当内存重叠时（第7行）我们自己的模拟实现无法像库函数一样正常处理重叠的情况，但这并不是必须要具备的能力，因为重叠有专门的函数memmove来处理。

//2️⃣memmove
//功能：
//memmove函数也是完成内存块拷贝的，和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
void* memmove(void* destination,const void* source,size_t num);
//参数：destination指向目标空间，拷贝的数据存放在这里；source指向源空间，要拷贝的数据从这里来；num要拷贝的数据占据的字节数
//返回值：拷贝完成后，返回目标空间的起始地址
//代码演示：
#include <string.h>
int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10};
    int brr[20]={0};
    memmove(brr,arr,40);
    int i;
    for(i=0;i<20;i++)
    {
        printf("%d ",brr[i]);//输出:1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 
    }
    printf("\n");
    memmove(arr+2,arr,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",arr[i]);//输出:1 2 1 2 3 4 7 8 9 10
    }
    printf("\n");
    int crr[]={1,2,3,4,5,6,7,8,9,10};
    memmove(crr,crr+2,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",crr[i]);//输出:3 4 5 6 5 6 7 8 9 10
    }
    return 0;
}
//模拟实现：
//错误版：
void *my_memmove(void *dest,void *str,size_t num)
{
    int i;
    for(i=num-1;i>=0;i--)
    {
        *((char*)dest+i)=*((char*)str+i);
    }
    return dest;
}
int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10};
    int brr[20]={0};
    my_memmove(brr,arr,40);
    int i;
    for(i=0;i<20;i++)
    {
        printf("%d ",brr[i]);//输出:1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 
    }
    printf("\n");
    my_memmove(arr+2,arr,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",arr[i]);//输出:1 2 1 2 3 4 7 8 9 10
    }
    printf("\n");
    int crr[]={1,2,3,4,5,6,7,8,9,10};
    my_memmove(crr,crr+2,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",crr[i]);//输出:5 6 5 6 5 6 7 8 9 10   这里错了
    }
    return 0;
}
//正确版：
//①
void* my_memmove(void *dest,void *src,size_t num)
{
    int i;
    if(dest<src) //前——>后
    {
        for(i=0;i<num;i++)
        {
            *((char*)dest+i)=*((char*)src+i);
        }
    }
    else         //后——>前
    {
        for(i=num-1;i>=0;i--)
        {
            *((char*)dest+i)=*((char*)src+i);
        }
    }
    return dest;
}
int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10};
    int brr[20]={0};
    my_memmove(brr,arr,40);
    int i;
    for(i=0;i<20;i++)
    {
        printf("%d ",brr[i]);//输出:1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 
    }
    printf("\n");
    my_memmove(arr+2,arr,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",arr[i]);//输出:1 2 1 2 3 4 7 8 9 10
    }
    printf("\n");
    int crr[]={1,2,3,4,5,6,7,8,9,10};
    my_memmove(crr,crr+2,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",crr[i]);//输出:3 4 5 6 5 6 7 8 9 10
    }
    return 0;
}

void* my_memmove(void *dest,void *src,size_t num)
{
    void *p=dest;
    if(dest<src)
    {
        while(num--)
        {
            *((char*)dest)=*((char*)src);
            dest=(char*)dest+1;
            src=(char*)src+1;
        }
    }
    else
    {
        while(num--) //num--是先使用，再-- 。所以相当于while(num),num=num-1
        {
            *((char*)dest+num)=*((char*)src+num); //所以此时+num的这个num是已经-1的num。
        }
    }
    return p;
}
int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10};
    int brr[20]={0};
    my_memmove(brr,arr,40);
    int i;
    for(i=0;i<20;i++)
    {
        printf("%d ",brr[i]);//输出:1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 
    }
    printf("\n");
    my_memmove(arr+2,arr,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",arr[i]);//输出:1 2 1 2 3 4 7 8 9 10
    }
    printf("\n");
    int crr[]={1,2,3,4,5,6,7,8,9,10};
    my_memmove(crr,crr+2,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",crr[i]);//输出:3 4 5 6 5 6 7 8 9 10
    }
    return 0;
}
//②
void* my_memmove(void* dst,const void* src,size_t count)
{
    void* ret = dst;
    if(dst<=src||(char*)dst>=((char*)src+count)) 
    {
        while(count--) 
        {
            *(char *)dst = *(char *)src;
            dst = (char *)dst + 1;
            src = (char *)src + 1;
        }
    }
    else 
    {
        dst = (char *)dst + count - 1;
        src = (char *)src + count - 1;
        while (count--) 
        {
            *(char *)dst = *(char *)src;
            dst = (char *)dst - 1;
            src = (char *)src - 1;
        }
    }
    return(ret);
}
int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10};
    int brr[20]={0};
    my_memmove(brr,arr,40);
    int i;
    for(i=0;i<20;i++)
    {
        printf("%d ",brr[i]);//输出:1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 
    }
    printf("\n");
    my_memmove(arr+2,arr,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",arr[i]);//输出:1 2 1 2 3 4 7 8 9 10
    }
    printf("\n");
    int crr[]={1,2,3,4,5,6,7,8,9,10};
    my_memmove(crr,crr+2,16);
    for(i=0;i<10;i++)
    {
        printf("%d ",crr[i]);//输出:3 4 5 6 5 6 7 8 9 10
    }
    return 0;
}

//3️⃣memset   P50 P103
//功能：是用来设置内存块的内容的，将内存中指定长度的空间设置为特定的内容。
void* memset(void* ptr,int value,size_t num);
//参数：
//ptr：指针，指向要设置的内存空间，也就是存放了要设置的内存空间的起始地址。
//value：要设置的值，函数将会把value值转换成unsigned char的数据进行设置的，也就是以字节为单位来设置内存块的。
//num：要设置的内存长度，单位是字节。
//返回值：返回的是要设置的内存空间的起始地址。
//代码演示：
#include <string.h>
int main()
{
    char arr[]="hello xxd";
    memset(arr+4,'g',4);
    printf("%s\n",arr);    //输出:hellggggd
    int brr[5]={0};
    memset(brr,1,20);
    int i;
    for(i=0;i<5;i++)
    {
        printf("%x\n",brr[i]); //十六进制
    }
    return 0;
}
//输出:
//1010101
//1010101
//1010101
//1010101
//1010101

//4️⃣memcmp
//功能：比较指定的两块内存块的内容，比较从ptr1和ptr2指针指向的位置开始，向后的num个字节。
int memcmp(const void* ptr1,const void* ptr2,size_t num);
//参数：
//ptr1：指针，指向一块待比较的内存块
//ptr2：指针，指向另外一块待比较的内存块
//num：指定的比较长度，单位是字节
//返回值：
//第一个大于第二个，则返回大于0的数字；
//第一个等于第二个，则返回0；
//第一个小于第二个，则返回小于0的数字。
//代码演示：
#include <string.h>
int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10};
    int brr[]={1,2,3,4,8};
    int r=memcmp(arr,brr,16);
    if(r>0)
    printf("arr>brr\n");
    else if(r<0)
    printf("arr<brr\n");
    else
    printf("arr=brr\n"); //输出:arr=brr
    int d=memcmp(arr,brr,17);
    if(d>0)
    printf("arr>brr\n");
    else if(d<0)
    printf("arr<brr\n"); //输出:arr<brr
    else
    printf("arr=brr\n");
    return 0;
}
//⚠️注意memcmp是按内存字节序列逐字节比较，而不是比较数值大小，数值大小会受大小端影响。